﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using FarseerGames.FarseerPhysics;
using UnitLibrary.GameObjects;
using UnitLibrary;
using FarseerGames.FarseerPhysics.Collisions;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Input;
using UnitLibrary.Utility;
using UnitLibrary.ScreenSystem;
using UnitLibrary.GameObjects.Units;
using System.Threading;

namespace UnitLibrary.Managers
{
    public delegate void PlayerDied();

    public class ComponentM: IManager
    {
        public PlayerDied playerDied;

        public ComponentM(Game game, PhysicsSimulator physicsSimulator, SpriteBatch spriteBatch)
        {
            _game = game;
            //Set the Content directory to content Directory
            //_game.Content.RootDirectory += "/Content";
            _physicsSimulator = physicsSimulator;
            SPRITEBATCH = spriteBatch;
            
            
            
        }

        #region Private and Public Variables
        private Game _game;
        private SpriteBatch SPRITEBATCH;
        private BulletM _bulletManager2;
        private TowerM _towerManager2;
        private PlayerM _playerManager2;
        private EnemyM _enemyManager2;
        private Camera2D _cameraManager;
        private MapM _mapManager;
        private PhysicsSimulator _physicsSimulator;
        public InputHelper inputHelper;
        public ParticleEffectsM particleEffects;
        private PowerUpPickUp _powerUpPickUps;
        private PowerUpEffects _powerUpEffects;
        private PointsM _pointsM;
        private List<IManager> managers;

        //todo: implement a queue so that all the managers don't have to be added to the game collection, have this class be the game loop, it will reduce the usage of the game object and free memory
        public Game Game
        {
            get { return _game; }
        }

        public ContentManager ContentManager
        {
            get { return _game.Content; }
        }

        public SpriteBatch SpriteBatch
        {
            get { return SPRITEBATCH; }
        }

        public BulletM bulletManager2
        {
            get { return _bulletManager2; }
        }
        public TowerM towerManager
        {
            get { return _towerManager2; }
        }
        public PlayerM playerManager
        {
            get { return _playerManager2; }
        }
        public EnemyM enemyManager
        {
            get { return _enemyManager2; }
        }
        //public static AIM aiManager
        //{
        //    get { return _aiManager; }
        //}       
        public Camera2D cameraManager
        {
            get { return _cameraManager; }
        }
        public MapM mapManager
        {
            get { return _mapManager; }
        }
        public PhysicsSimulator physicsSimulator
        {
            get { return _physicsSimulator; }
        }

        public PowerUpPickUp powerUpPickUp
        {
            get { return _powerUpPickUps; }
        }

        public PowerUpEffects powerUpEffects
        { get { return _powerUpEffects; } }

        public PointsM pointsM
        { get { return _pointsM; } }
#endregion

        #region Create and Destroy Components
        public void LoadContent()
        {
                     
            inputHelper = new InputHelper();
            _mapManager = new MapM(_game, this, 3000, 3000, 10);
            _bulletManager2 = new BulletM(_game, this, 50);
            _towerManager2 = new TowerM(_game, this, 10);
            _playerManager2 = new PlayerM(_game, this);
            _enemyManager2 = new EnemyM(_game, this); 
           
            particleEffects = new ParticleEffectsM(Game, this);
            Vector2 size = new Vector2(1280, 768); Vector2 position = Vector2.Zero;float movespeed = 1f;float rotation = 0f;float rotationrate = .1f;float zoom = 1f;float zoomrate = .1f;float minzoom = .25f;float maxzoom = 5f;Vector2 minposition = Vector2.Zero;Vector2 maxposition = new Vector2(_mapManager.WorldWidth, _mapManager.WorldHeight);
            _cameraManager = new Camera2D(size, position, movespeed, rotation, rotationrate, zoom, zoomrate, minzoom, maxzoom, minposition, maxposition);
            _powerUpPickUps = new PowerUpPickUp(_game, this);
            _powerUpEffects = new PowerUpEffects(_game, this);
            _pointsM = new PointsM(_game, this);

             managers = new List<IManager>();
            managers.Add(_mapManager);           
            managers.Add(_playerManager2);           
            managers.Add(_enemyManager2);          
            managers.Add(_towerManager2);            
            managers.Add(_bulletManager2);
            managers.Add(_powerUpPickUps);
            managers.Add(_powerUpEffects);
            managers.Add(_pointsM);

            managers.Add(particleEffects);
            
            foreach (IManager manager in managers)
            {
                manager.LoadContent();
            }
        }
#endregion

        /// <summary>
        /// This region will hold public methods for the managers to call.
        /// Managers will call these methods to get stuff done. They will not call each other.
        /// All data transfer will go through this class.
        /// </summary>
        #region Arbiter Methods
        public void RemoveUnit(Iid id, bool isUnitKilled)
        {
            switch (id.UnitID)
            {
                case UnitID.Bullet:
                    _bulletManager2.Remove(id);
                    break;
                case UnitID.Enemy:
                    _enemyManager2.Remove(id);
                    break;
                case UnitID.Tower:
                    if (id.NodeID > 0)
                        _towerManager2.Remove(id);
                    else
                        _towerManager2.RemoveLastUnit();
                    break;
                case UnitID.Points:
                    _pointsM.Remove(id);
                    break;
                case UnitID.PowerUp:
                    _powerUpPickUps.Remove(id);
                    break;
                default:
                    throw new Exception ("Invalid Unit ID!");                    
            }
        }

        public void CreateUnit(UnitID type, string unitName, Vector2 startPosition, Vector2 direction)
        {
            switch (type)
            {
                case UnitID.Bullet:
                    _bulletManager2.Create(unitName, startPosition, direction);
                    break;
                case UnitID.Enemy:
                    _enemyManager2.Create(unitName, startPosition, direction);
                    break;
                case UnitID.Tower:
                    _towerManager2.Create(unitName, startPosition, direction);
                    break;
                case UnitID.Points:
                    _pointsM.Create(unitName, startPosition, direction);
                    break;
                case UnitID.PowerUp:
                    _powerUpEffects.Apply(unitName, string.Empty);
                    break;
                default:
                    throw new Exception("Invalid unit type!");
            }
        }

        public Vector2 UnitPosition(UnitID type, int nodeID)
        {
            Vector2 unitPosition = Vector2.Zero;
            switch (type)
            {
                case UnitID.Player:
                    unitPosition = _playerManager2.Player.Position;
                    break;
                case UnitID.Enemy:
                    unitPosition = _enemyManager2.GetUnitPosition(nodeID);
                    break;
                case UnitID.Bullet:
                    unitPosition = _enemyManager2.GetUnitPosition(nodeID);
                    break;
                case UnitID.Tower:
                    unitPosition = _towerManager2.GetUnitPosition(nodeID);
                    break;
                default:
                    throw new Exception("Invalid unit type!");
            }
            return unitPosition;
        }

        public int GetDamage(Iid id)
        {
            try
            {
                switch (id.UnitID)
                {
                    case UnitID.Bullet:
                        return _bulletManager2.UnitPool.Get(id.NodeID).Item.Damage;
                    case UnitID.Enemy:
                        return _enemyManager2.UnitPool.Get(id.NodeID).Item.Damage;
                    case UnitID.Tower:
                        return _towerManager2.UnitPool.Get(id.NodeID).Item.Damage;
                    case UnitID.Player:
                        return _playerManager2.Player.Damage;
                    default:
                        return 0;
                }
            }
            catch (Exception e)
            {
                return 0;
            }
        }

        public object GetUnit(Iid id)
        {
            try
            {
                switch (id.UnitID)
                {
                    case UnitID.Bullet:
                        return _bulletManager2.UnitPool.Get(id.NodeID).Item;
                    case UnitID.Enemy:
                        return _enemyManager2.UnitPool.Get(id.NodeID).Item;
                    case UnitID.Tower:
                        return _towerManager2.UnitPool.Get(id.NodeID).Item;
                    case UnitID.Player:
                        return _playerManager2.Player;
                    case UnitID.Points:
                        return _pointsM.UnitPool.Get(id.NodeID).Item;
                    case UnitID.PowerUp:
                        return  _powerUpPickUps.UnitPool.Get(id.NodeID).Item;
                    default:
                        throw new ArgumentOutOfRangeException("Wrong type of unit");
                }
            }
            catch (Exception e)
            {
                return null;
            }
        }

        #endregion

       
        public void Update(GameTime gameTime)
        {
            inputHelper.Update();
            _cameraManager.TrackingBody = _playerManager2.Player.BODY;
            _cameraManager.Update(inputHelper); 
            foreach (IManager component in managers)
            {
                component.Update(gameTime);
            }
            _physicsSimulator.Update((float)gameTime.ElapsedGameTime.TotalMilliseconds * .001f);
        }

        public void Draw(SpriteBatch _spriteBatch)
        {
            _spriteBatch.Begin(SpriteBlendMode.Additive, SpriteSortMode.BackToFront, SaveStateMode.None, _cameraManager.CameraMatrix);
            foreach (IManager component in managers)
            {
                component.Draw(_spriteBatch);
            }
            _spriteBatch.End();
        }
    }
}
